{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 竞赛题目 \n",
    "[官网地址](https://tianchi.aliyun.com/competition/entrance/531871/information)  \n",
    "问题类型：时间序列预测问题。  \n",
    "基于历史气候观测和模式模拟数据，利用T时刻过去12个月(包含T时刻)的时空序列（气象因子），构建预测ENSO的深度学习模型，预测未来1-24个月的Nino3.4指数，如下图所示：\n",
    "![image.png](https://gitee.com/Little_Six/repository_pic/raw/master/image-20210401222211300.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据\n",
    "东经 0°–360°，南纬 55°到北纬 60°-> 维度（24，72）\n",
    "- 海平面温度（SST）  \n",
    "- 热含量异常(T300)  \n",
    "- 纬向风异常（Ua）  \n",
    "- 经向风异常（Va）  \n",
    "\n",
    "SODA数据：可以当成真实数据 100年\n",
    "\n",
    "CMIP5/CMIP6：可以当成模拟数据（大气模型模拟） CMIP5:  151年 *  15 个模式=2265     140年 * 17 个模式=2380\n",
    "\n",
    "Label: nino3.4指标\n",
    "\n",
    "SODA_train、SODA_label、CMIP_train、CMIP_label"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 评估指标\n",
    "评分细则说明：根据所提供的n个测试数据，对模型进行测试，得到n组未来1-24个月的序列选取对应预测时效的n个数据与标签值进行计算相关系数和均方根误差，如下图所示。并计算得分。  "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image-20210401224442956](https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401224442956.png)\n",
    "![](https://gitee.com/Little_Six/repository_pic/raw/master/null/161155717087414081611557170946.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 赛题理解"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Nino3.4（西经 170°–120°，南北纬 5°以内区域中的平均 SST 值）就是本次赛题的标签。  \n",
    "\n",
    "训练数据对应的标签数据是当前时刻Nino3.4 SST（170°–120°W，5°S–5°N）异常指数的三个月滑动平均值。三个月滑动平均值为当前月与未来两个月的平均值。红框内的区域SST（解释：计算出来当月的SST均值后，还需要计算未来两个月的SST均值，然后求平均才是本月的nino3.4指标）\n",
    "<img src=\"https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401224652219.png\" alt=\"image-20210401224652219\" style=\"zoom:50%;\" />"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据赛题本意，每一个月，在世界地图上（东经 0°–360°，南纬 55°到北纬 60°），标注了海平面温度（SST）、热含量异常(T300)、纬向风异常（Ua）、经向风异常（Va）数据，地图可以看做是一个4* 24* 72的图片（4个通道数代表SST/T300/UA/VA参数）。一个月对应了一张图片，而且每一张图片都能对应一个Nino3.4指数。  \n",
    "[参考EDA](https://gitee.com/Little_Six/aiweather-ocean-forecasts/blob/master/code/EDA.ipynb)  \n",
    "<img src=\"https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401224718756.png\" alt=\"image-20210401224718756\" style=\"zoom:50%;\" />"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "具体来说，需要根据一年（12个月）的气候变化（sst/t300/ua/va），推断出未来2年（24个月）的nino3.4指标。  \n",
    "思路一：根据12张图片（4 * 24 * 72）推断未来24张图片（4 * 24 * 72），再根据推断的图片，计算得到每个月的nino3.4指标（流程1）。  \n",
    "思路二：根据12张图片（4 * 24 * 72）直接预测未来2年（24个月）的nino3.4指标（流程2）。\n",
    "<img src=\"https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401224743945.png\" alt=\"image-20210401224743945\" style=\"zoom:50%;\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据探索"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 验证nino3.4标签   \n",
    "\n",
    "每一个月的nino3.4指标是根据每一个月的SST指数计算得来的吗（西经 170°–120°，南北纬 5°以内区域中的平均 SST 值，滑动三个月）？验证nino3.4指标是否是由此得来的。\n",
    "\n",
    "1. 提取西经 170°–120°，南北纬 5°以内区域中 SST 值，计算其与标签的关系。\n",
    "2. 根据测试结果，前24个月的SST指数均值滑动3个月，能够对应上nino3.4指标，但是之后的就对应不上了，但是总体相关系数达到0.991，平均绝对误差为0.087 左右，很奇怪，通过验证数据，没有发现问题所在。  \n",
    "3. 对CMIP_train数据做同样测试，用SST指数均值滑动3个月，能够对应上nino3.4指标，相关系数以及平均绝对误差都可以对应。是否SODA数据存在问题？CMIP5和CMIP6都经过测试。\n",
    "\n",
    "- SODA 数据只有前24个月的数据符合nino3.4指标的构建规则，后面100年的数据趋势符合，但是不能完全重合\n",
    "- CMIP 数据符合nino3.4指标的构建规则\n",
    "<img src=\"https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401230504313.png\" alt=\"image-20210401230504313\" />\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401225137389.png\" alt=\"image-20210401225137389\" />\n",
    "> 结论\n",
    "\n",
    "根据官方解释：  \n",
    "准确的label是根据标准1* 1的海温甚至更高精度的海温计算的，而本次比赛5* 5精度的海温计算的肯定有误差。  \n",
    "SODA前24个月的数据一致是因为前24个月没有1* 1的观测数据，只能用这个5* 5计算出来的代替，所以一致。  \n",
    "CMIP数据没有真实样本资料，而SODA观测是有真实样本的。所以SODA的label是更准确的。    \n",
    "数据是没有问题的 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  缺失值删除"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "样本个数大致可以看成:   \n",
    "CMIP5+CMIP6+SODA = 2265 +  2380 +100 = 4745  \n",
    "含有缺失值的样本个数有 775个  \n",
    "EDA中虽然说缺失值大多是陆地，但是还是将其删除了  \n",
    "\n",
    "![image-20210401225226811](https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401225226811.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型构建\n",
    "### 思路\n",
    "- 模型构建参考了Baseline ，主要思路是利用CNN+LSTM来构建,B榜最高的一次分数是26分。\n",
    "- 经纬度外加4个特征，可以看作图片的长、宽和通道(4,24,72)\n",
    "\n",
    "- B榜分两次进行训练\n",
    "- 第一次是CMIP数据划分8:2 进行训练     将含有空值的样本去除了  \n",
    "- 利用训练好的模型，在SODA数据上微调，SODA数据划分 8:2    \n",
    "\n",
    "[训练过程参考](https://gitee.com/Little_Six/aiweather-ocean-forecasts/blob/master/code/20210329_sub1.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class simpleSpatailTimeNN(nn.Module):\n",
    "    \n",
    "    def __init__(self, n_cnn_layer:int=1, kernals:list=[3], n_lstm_units:int=64):\n",
    "        super(simpleSpatailTimeNN, self).__init__()\n",
    "        self.conv1 = nn.Conv2d(4,4,3,padding = 1)\n",
    "        self.batch_norm = nn.BatchNorm1d(12, affine=False)\n",
    "        self.lstm = nn.LSTM(24*72*4, n_lstm_units, 2, bidirectional=True,batch_first=True)\n",
    "        self.pool3 = nn.AdaptiveAvgPool2d((1, 128))\n",
    "        self.linear = nn.Linear(128, 24)\n",
    "        \n",
    "    def forward(self, sst, t300, ua, va):\n",
    "        Seqs = []\n",
    "        for i in range(12):\n",
    "            sst_tmp = sst[:,i,:,:].unsqueeze(1)   #[batch,1,24,72]\n",
    "            t300_tmp = t300[:,i,:,:].unsqueeze(1)\n",
    "            ua_tmp = ua[:,i,:,:].unsqueeze(1)\n",
    "            va_tmp = va[:,i,:,:].unsqueeze(1)\n",
    "            seq1 = torch.cat([sst_tmp,t300_tmp,ua_tmp,va_tmp],dim=1)  #[batch,4,24,72]\n",
    "            seq1 = self.conv1(seq1)#[batch,4,24,72]\n",
    "            seq1 = torch.flatten(seq1, start_dim=1).unsqueeze(1)#[batch,4*24*72]\n",
    "            Seqs.append(seq1)#    [batch,1,4*24*72]  *  12\n",
    "        x = torch.cat([Seqs[i] for i in range(12)], dim=1)    #[batch,12,4*24*72]  \n",
    "        x = self.batch_norm(x)\n",
    "        x, _ = self.lstm(x)\n",
    "        x = self.pool3(x).squeeze(dim=-2)\n",
    "        x = self.linear(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 几次提交记录"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image-20210401231621484](https://gitee.com/Little_Six/repository_pic/raw/master/null/image-20210401231621484.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 其他尝试\n",
    "- 使用SST训练\n",
    "- 使用SST+T300训练\n",
    "- 提取前12个月的nino3.4指数，使用全连接层直接训练\n",
    "- 使用Resnet18模型直接输出,线上-1.3573分  \n",
    "- 使用ConvLstm模型，废了很大劲修改模型，线上22.7分  \n",
    "- 使用6个月数据进行训练，1-6、2-7... 7-12分别进行训练，相当于构建了7个模型进行融合线上10分  \n",
    "- 使用滑窗构建数据（测试集开始月份是随机的，并不是都从1月份开始），一共可以构建接近5W个样本，进行训练，效果很差。\n",
    "- 因为数据验证波动很大，所以在验证集如何构建上变化了很多次，最终选择了SODA再训练验证方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Docker常用操作\n",
    "1. 进入镜像仓库https://cr.console.aliyun.com ，创建一个命名空间、创建一个镜像（复制镜像地址）\n",
    "2. 在本机安装docker,安装完成后，使用命令行登录阿里云的仓库，输入密码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#登录  \n",
    "docker login --username=xxx registry.cn-hangzhou.aliyuncs.com\n",
    " \n",
    "#在本地构建镜像   本地Dockerfile文件构建   \n",
    "#镜像地址registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit     \n",
    "docker build -t registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:1.0 .   \n",
    "\n",
    "#开启一个bash命令行，运行本地镜像   \n",
    "docker run -it registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:1.0 bash   \n",
    "docker run -it registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:1.0 sh run.sh     \n",
    "\n",
    "#推送镜像到云端   \n",
    "docker push registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:1.0 \n",
    "\n",
    "#挂在数据目录      -v 本地目录:镜像地址   当前目录用/  绝对目录用\\  \n",
    "docker run -it -v /tcdata/enso_round1_test_20210201/:/tcdata/enso_round1_test_20210201/ registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:4.0 sh run.sh\n",
    "\n",
    "docker run -it -v D:\\jupyter\\WeatherOceanForecasts\\tcdata\\enso_round1_test_20210201\\:/tcdata/enso_round1_test_20210201/ registry.cn-hangzhou.aliyuncs.com/littlesix/ai_submit_pytorch:1.0 sh run.sh       \n",
    "\n",
    "docker run -it -v D:\\jupyter\\WeatherOceanForecasts\\tcdata\\enso_round1_test_20210201\\:/tcdata/enso_round1_test_20210201/ registry.cn-hangzhou.aliyuncs.com/littlesix/ai_submit_pytorch:1.0 bash \n",
    "\n",
    "#提交容器 形成新镜像   \n",
    "docker commit contain_id registry.cn-hangzhou.aliyuncs.com/littlesix/ai_earth_submit:6.0 \n",
    "\n",
    "#在容器内删除tcdata文件夹   \n",
    "rm tcdata -r \n",
    "\n",
    "#向容器中添加文件   \n",
    "docker cp C:\\Users\\Administrator\\Desktop\\wheather\\mlp_predict.py contain_id:./ \n",
    "docker cp D:\\jupyter\\WeatherOceanForecasts\\user_data\\ref.pt contain_id:./user_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结：\n",
    "1. 前期一定一定要确定自己的baseline以及线下验证方式。如果有相关比赛SOTA（state-of-the-art），或者baseline，会节省很多时间。\n",
    "2. 每次提交就是一次实验，实验记录一定要记清楚，所使用的方法，特征，模型等，形成一个单独的版本，方便复盘与思考。\n",
    "\n",
    "\n",
    "通过这次比赛:\n",
    "1. 学习了pytorch的常用操作。数据集构建、模型构建、训练、测试等。\n",
    "2. 学习了RNN/LSTM的使用方式。\n",
    "3. 学习了pytorch中已有模型的修改方式，如Resnet18等。\n",
    "4. 学习了ConvLstm的使用方式，只能跑通，但是对使用效果、修改模型等还需要进一步了解。\n",
    "5. 巩固和学习了docker的使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考\n",
    "1. [Datewhale_Baseline](https://github.com/datawhalechina/team-learning-data-mining/tree/master/WeatherOceanForecasts)  \n",
    "2. [CNN+LSTM_baselineNINO预测：22分baseline](https://tianchi.aliyun.com/forum/postDetail?postId=176735)    \n",
    "3. [「EDA」时空问题之厄尔尼诺预测](https://tianchi.aliyun.com/forum/postDetail?postId=177158)  \n",
    "4. [从0梳理1场时间序列赛事！](https://mp.weixin.qq.com/s/63LPCHNo4zOA_UGDAc2xUQ)  \n",
    "5. [气象遇见机器学习](https://bbs.huaweicloud.com/blogs/113047)  \n",
    "6. [交叉新趋势|采用神经网络与深度学习来预报降水、温度等案例(附代码/数据/文献)](https://cloud.tencent.com/developer/article/1475570)  \n",
    "7. [最新进展 | 深度学习在天气预测中的应用](https://blog.csdn.net/xixiaoyaoww/article/details/104548747)  \n",
    "8. [基于气象模式、气象观测数据的深度学习预报方法总结（Deecamp 结营总结）](https://blog.csdn.net/maliang_1993/article/details/99622197)  \n",
    "9. [预测伦敦空气质量convlstm](https://github.com/RobinLuoNanjing/air_pollutants_prediction_lstm)  \n",
    "10. [ConvLSTM_pytorch](https://github.com/ndrplz/ConvLSTM_pytorch)  \n",
    "11. [PyTorch_ConvLSTM代码（参数）解读笔记](https://my.oschina.net/u/4259809/blog/4702405)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[代码地址](https://gitee.com/Little_Six/aiweather-ocean-forecasts)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
